package com.tengju.motivation.domain.shared;

import org.apache.tomcat.jni.Local;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;


public class DateUtil {

    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_TIME_FORMAT_FILE_NAME = "MM月dd日HH点mm分";
    public static final String DATE_TIME_MINUTE_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DATE_DAY_FORMAT = "yyyy-MM-dd";
    public static final String DATE_MONTH_FORMAT = "yyyy-MM";
    public static final String DATE_CHINESE_FORMAT = "yyyy年MM月dd日";
    public static final String DATE_FORMAT = "yyyyMMdd";
    public static final String DATE_FORMAT_MONTH_INT = "yyyyMM";
    public static final String DATE_FORMAT_SECOND_INT = "yyyyMMddHHmmss";
    public static final String QUARTER_SIGN = "Q";


    private DateUtil() {
        //
    }

    public static Long getCurrentTime() {
        return DateUtil.localDateTimeToDate(LocalDateTime.now()).getTime();
    }

    public static Date parseStringToDate(String format, String time) {
        try {
            if (StringUtils.isEmpty(time)) {
                return null;
            }
            SimpleDateFormat dateFormat = new SimpleDateFormat(format);
            return dateFormat.parse(time);
        } catch (ParseException e) {
            throw new DateParseException(String.format("format: %s, time: %s", format, time));
        }
    }

    public static String parseDateToString(String format, Date date) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(date);
    }

    /**
     * 解析日期默认格式
     */
    public static LocalDateTime format(String dateTime, String pattern) {
        return dateToLocalDateTime(parseStringToDate(pattern, dateTime));
    }

    public static String format(LocalDateTime localDateTime) {
        return formatLocalDateTime(DATE_TIME_FORMAT, localDateTime);
    }

    public static Date formatDate(String format, Date date) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return parseStringToDate(format, sdf.format(date));
    }

    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static LocalDateTime dateToLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    public static String formatLocalDateTime(String format, LocalDateTime localDateTime) {
        return localDateTime.format(DateTimeFormatter.ofPattern(format));
    }

    public static String formatLocalDate(String format, LocalDate localDate) {
        return localDate.format(DateTimeFormatter.ofPattern(format));
    }


    /**
     * 将LocalDate 转换成 LocalDateTime
     *
     * @param localDate 日期
     * @return {@link LocalDateTime}
     */
    public static LocalDateTime toLocalDateTime(LocalDate localDate){
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
        return LocalDateTime.from(zdt.toInstant());
    }


    /**
     * 前/后?天
     */
    public static Date rollDay(Date d, int day) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(d);
        cal.add(Calendar.DAY_OF_MONTH, day);
        return cal.getTime();
    }

    public static String formatMonthToStr(Integer month) {
        Date date = parseStringToDate(DATE_FORMAT_MONTH_INT, month + "");
        return parseDateToString(DATE_MONTH_FORMAT, date);
    }

    /**
     * 获取当月第一天的最小时间
     *
     * @return 当月第一天的最小时间
     */
    public static LocalDateTime getFirstDayOfMonthMin() {
        LocalDateTime date = LocalDateTime.now();
        LocalDateTime firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
        return LocalDateTime.of(firstDay.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取某个月的最小时间
     *
     * @return 获取某个月的最小时间
     */
    public static LocalDateTime getMonthMinOfMonth(LocalDateTime localDateTime) {
        LocalDateTime firstDay = localDateTime.with(TemporalAdjusters.firstDayOfMonth());
        return LocalDateTime.of(firstDay.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取某个月的最小时间
     *
     * @return 获取某个月的最小时间
     */
    public static LocalDateTime getMonthMin(Integer month) {
        LocalDateTime format = format(String.valueOf(month), DATE_FORMAT_MONTH_INT);
        LocalDateTime firstDay = format.with(TemporalAdjusters.firstDayOfMonth());
        return LocalDateTime.of(firstDay.toLocalDate(), LocalTime.MIN);
    }

    public static Integer getMonthInt(LocalDateTime localDateTime) {
        return Integer.valueOf(localDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT_MONTH_INT)));
    }

    public static Integer getDayIntByLocalDate(LocalDate localDate) {
        return Integer.valueOf(localDate.format(DateTimeFormatter.ofPattern(DATE_FORMAT)));
    }

    public static Long getMonthLong(LocalDateTime localDateTime) {
        return Long.valueOf(localDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT_MONTH_INT)));
    }

    public static Long getSecondLong(LocalDateTime localDateTime) {
        return Long.valueOf(localDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT_SECOND_INT)));
    }

    /**
     * 获取下个月的最小时间
     *
     * @return 获取下个月的最小时间
     */
    public static LocalDateTime getNextMonthMin(LocalDateTime localDateTime) {
        LocalDateTime nextMonth = localDateTime.plusMonths(1);
        return LocalDateTime.of(nextMonth.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取下个月的最小时间
     *
     * @return 获取下个月的最小时间
     */
    public static LocalDateTime getNextMonthMin(Integer month) {
        LocalDateTime format = format(String.valueOf(month), DATE_FORMAT_MONTH_INT);
        LocalDateTime nextMonth = format.plusMonths(1);
        return LocalDateTime.of(nextMonth.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取当月第后天的最大时间
     *
     * @return 当月第后天的最大时间
     */
    public static LocalDateTime getLastDayOfMonthMax() {
        LocalDateTime date = LocalDateTime.now();
        LocalDateTime lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
        return LocalDateTime.of(lastDay.toLocalDate(), LocalTime.MAX);
    }

    /**
     * 获取某个月的最大时间
     *
     * @return 获取某个月的最大时间
     */
    public static LocalDateTime getMonthMax(Integer month) {
        LocalDateTime format = format(String.valueOf(month), DATE_FORMAT_MONTH_INT);
        LocalDateTime lastDay = format.with(TemporalAdjusters.lastDayOfMonth());
        return LocalDateTime.of(lastDay.toLocalDate(), LocalTime.MAX);
    }

    /**
     * 获取当前时间几个月后的最小时间
     *
     * @param monthsToAdd 几个月后
     * @return 当前时间几个月后的最小时间
     */
    public static LocalDateTime getPlusMonthsMin(int monthsToAdd) {
        LocalDate localDate = LocalDate.now().withDayOfMonth(1);
        LocalDate plusMonths = localDate.plusMonths(monthsToAdd);
        return LocalDateTime.of(plusMonths, LocalTime.MIN);
    }

    /**
     * 获取当前时间几个月前的最小时间
     *
     * @param monthsToSubtract 几个月前
     * @return 当前时间几个月前的最小时间
     */
    public static LocalDateTime getMinusMonthsMin(int monthsToSubtract) {
        LocalDate localDate = LocalDate.now().withDayOfMonth(1);
        LocalDate minusMonths = localDate.minusMonths(monthsToSubtract);
        return LocalDateTime.of(minusMonths, LocalTime.MIN);
    }

    /**
     * 获取月度最后一天
     */
    public static String getLastMonthDay(LocalDateTime localDateTime) {
        LocalDateTime lastDay = localDateTime.with(TemporalAdjusters.lastDayOfMonth());
        return formatVisitDate(lastDay);
    }

    /**
     * 解析日期默认格式  yyyy-MM-dd
     */
    public static String formatVisitDate(LocalDateTime localDateTime) {
        return DateUtil.formatLocalDateTime(DATE_DAY_FORMAT, localDateTime);
    }

    /**
     * 获取季度的最小时间 如：参数 2021Q1 返回 2020-01-01 00:00:00
     *
     * @param dateStr 如：2021Q1
     * @return 季度的最小时间
     */
    public static LocalDateTime getQuarterMin(String dateStr) {
        if (StringUtils.isEmpty(dateStr) || dateStr.length() != 6) {
            throw new DateParseException(dateStr);
        }
        String year = dateStr.substring(0, 4);
        String quarter = dateStr.substring(4, 6);
        String minMonthsOfQuarter = getMinMonthsOfQuarter(quarter);
        String[] split = minMonthsOfQuarter.split("-");
        LocalDate of = LocalDate
                .of(Integer.parseInt(year), Integer.parseInt(split[0]), Integer.parseInt(split[1]));
        return LocalDateTime.of(of, LocalTime.MIN);
    }

    /**
     * 获取下个季度的最小时间 如：参数 2021Q4 返回 2022-01-01T00:00
     *
     * @param dateStr 如：2021Q4
     * @return 获取下个季度的最小时间
     */
    public static LocalDateTime getNextQuarterMin(String dateStr) {
        if (StringUtils.isEmpty(dateStr) || dateStr.length() != 6) {
            throw new DateParseException(dateStr);
        }
        LocalDateTime quarterMax = getQuarterMax(dateStr);
        LocalDateTime localDateTime = quarterMax.plusDays(1);
        return LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取季度的最大时间 如：参数 2021Q1 返回 2021-03-31 23:59:59
     *
     * @param dateStr 如：2021Q1
     * @return 季度的最大时间
     */
    public static LocalDateTime getQuarterMax(String dateStr) {
        if (StringUtils.isEmpty(dateStr) || dateStr.length() != 6) {
            throw new DateParseException(dateStr);
        }
        String year = dateStr.substring(0, 4);
        String quarter = dateStr.substring(4, 6);
        String maxMonthsOfQuarter = getMaxMonthsOfQuarter(quarter);
        String[] split = maxMonthsOfQuarter.split("-");
        LocalDate of = LocalDate
                .of(Integer.parseInt(year), Integer.parseInt(split[0]), Integer.parseInt(split[1]));
        return LocalDateTime.of(of, LocalTime.MAX);
    }

    public static String getMinMonthsOfQuarter(String quarter) {
        String result = "";
        switch (quarter.toUpperCase()) {
            case "Q1":
                result = "01-01";
                break;
            case "Q2":
                result = "04-01";
                break;
            case "Q3":
                result = "07-01";
                break;
            case "Q4":
                result = "10-01";
                break;
            default:
                throw new DateParseException(quarter);
        }
        return result;
    }

    public static String getMaxMonthsOfQuarter(String quarter) {
        String result = "";
        switch (quarter.toUpperCase()) {
            case "Q1":
                result = "03-31";
                break;
            case "Q2":
                result = "06-30";
                break;
            case "Q3":
                result = "09-30";
                break;
            case "Q4":
                result = "12-31";
                break;
            default:
                throw new DateParseException(quarter);
        }
        return result;
    }

    /**
     * 获取季度的月份列表 如：参数 2021Q1 返回 [202101,202102,202103]
     *
     * @param dateStr 如：2021Q1
     * @return 季度的月份列表
     */
    public static List<Integer> getYearMonthListOfQuarter(String dateStr) {
        if (StringUtils.isEmpty(dateStr) || dateStr.length() != 6) {
            throw new DateParseException(dateStr);
        }
        String year = dateStr.substring(0, 4);
        String quarter = dateStr.substring(4, 6);
        List<String> monthStrList = getMonthsOfQuarter(quarter);
        return monthStrList.stream()
                .map(monthStr -> Integer.parseInt(year + monthStr))
                .collect(Collectors.toList());
    }

    /**
     * 获取季度的月份列表 如：参数 2021Q1 返回 [2021-01,2021-02,2021-03]
     *
     * @param dateStr 如：2021Q1
     * @return 季度的月份列表
     */
    public static List<String> getYearMonthStringListOfQuarter(String dateStr) {
        if (StringUtils.isEmpty(dateStr) || dateStr.length() != 6) {
            throw new DateParseException(dateStr);
        }
        String year = dateStr.substring(0, 4);
        String quarter = dateStr.substring(4, 6);
        List<String> monthStrList = getMonthsOfQuarter(quarter);
        return monthStrList.stream()
                .map(monthStr -> year + "-" + monthStr)
                .collect(Collectors.toList());
    }

    public static List<String> getMonthsOfQuarter(String quarter) {
        switch (quarter.toUpperCase()) {
            case "Q1":
                return List.of("01", "02", "03");
            case "Q2":
                return List.of("04", "05", "06");
            case "Q3":
                return List.of("07", "08", "09");
            case "Q4":
                return List.of("10", "11", "12");
            default:
                throw new DateParseException(quarter);
        }
    }

    public static Integer getMonthOfQuarter() {
        String now = formatLocalDateTime(DATE_MONTH_FORMAT, LocalDateTime.now());
        String[] split = now.split("-");
        switch (split[1]) {
            case "01":
            case "04":
            case "07":
            case "10":
                return 1;
            case "02":
            case "05":
            case "08":
            case "11":
                return 2;
            case "03":
            case "06":
            case "09":
            case "12":
                return 3;
            default:
                throw new DateParseException();
        }
    }

    public static List<String> getQuarterList() {
        List<String> result = new ArrayList<>();
        List<String> monthsList = new ArrayList<>();
        LocalDate localDate = LocalDate.now();
        monthsList.add(formatLocalDate(DATE_FORMAT_MONTH_INT, localDate));
        while (true) {
            localDate = localDate.minusMonths(1);
            monthsList.add(formatLocalDate(DATE_FORMAT_MONTH_INT, localDate));
            if ("202101".equals(formatLocalDate(DATE_FORMAT_MONTH_INT, localDate))) {
                break;
            }
        }
        for (String month : monthsList) {
            String quarter = getYearQuarterByYearMonth(month);
            if (!result.contains(quarter)) {
                result.add(quarter);
            }
        }
        return result;
    }

    public static String getQuarterOrMonthStr(Long startTime, Long endTime) {
        String result = "";
        if (startTime.toString().length() == 6 && endTime.toString().length() == 6) {
            if (startTime.equals(endTime)) {
                // 相同则为月份
                return String.valueOf(startTime);
            } else if (format(startTime.toString(), DATE_FORMAT_MONTH_INT).plusMonths(2).isEqual(format(endTime.toString(), DATE_FORMAT_MONTH_INT))) {
                // 差三个月，则为季度
                if (getYearQuarterByYearMonth(String.valueOf(startTime)).equals(getYearQuarterByYearMonth(String.valueOf(endTime)))) {
                    return getYearQuarterByYearMonth(String.valueOf(startTime));
                } else {
                    throw new DateParseException();
                }
            }
        }
        return result;
    }

    private static String getYearQuarterByYearMonth(String months) {
        String year = months.substring(0, 4);
        String month = months.substring(4, 6);
        return year + getQuarterByMonth(month);
    }

    private static String getQuarterByMonth(String month) {
        switch (month) {
            case "01":
            case "02":
            case "03":
                return "Q1";
            case "04":
            case "05":
            case "06":
                return "Q2";
            case "07":
            case "08":
            case "09":
                return "Q3";
            case "10":
            case "11":
            case "12":
                return "Q4";
            default:
                throw new DateParseException();
        }
    }

    public static Map<String, Integer> getDateTypeMap() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("月度", 1);
        map.put("季度", 2);
        return map;
    }

    /**
     * 获取日期的最小时间
     *
     * @param dateType
     * @param date
     * @return
     */
    public static LocalDateTime getLocalDateTimeMinByDateType(Integer dateType, String date) {
        if (getDateTypeMap().get("月度").equals(dateType)) {
            LocalDateTime format = format(date, DATE_FORMAT_MONTH_INT);
            return getMonthMinOfMonth(format);
        } else if (getDateTypeMap().get("季度").equals(dateType)) {
            return getQuarterMin(date);
        } else {
            return null;
        }
    }

    /**
     * 获取下一个日期的最小时间
     *
     * @param dateType
     * @param date
     * @return
     */
    public static LocalDateTime getNextLocalDateTimeMinByDateType(Integer dateType, String date) {
        if (getDateTypeMap().get("月度").equals(dateType)) {
            LocalDateTime format = format(date, DATE_FORMAT_MONTH_INT);
            return getNextMonthMin(format);
        } else if (getDateTypeMap().get("季度").equals(dateType)) {
            return getNextQuarterMin(date);
        } else {
            return null;
        }
    }

    public static void main(String[] args) {
        LocalDate localDate = LocalDate.now().withDayOfMonth(1);
        LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN);
        LocalDateTime max = LocalDateTime.of(localDate, LocalTime.MAX);
        LocalDate nextMonth = localDate.plusMonths(1);
        LocalDateTime end = LocalDateTime.of(nextMonth, LocalTime.MIN);
        String minStr = DateUtil
                .formatLocalDateTime(DateUtil.DATE_TIME_FORMAT, DateUtil.getFirstDayOfMonthMin());
        String maxStr = DateUtil
                .formatLocalDateTime(DateUtil.DATE_TIME_FORMAT, DateUtil.getLastDayOfMonthMax());

        Date startTime = DateUtil.localDateTimeToDate(DateUtil.getFirstDayOfMonthMin());
        Date endTime = DateUtil.localDateTimeToDate(DateUtil.getLastDayOfMonthMax());
        Date plus = DateUtil.localDateTimeToDate(DateUtil.getPlusMonthsMin(3));
        LocalDateTime quarterMin = getQuarterMin("2020Q1");
        LocalDateTime quarterMax = getQuarterMax("2021Q1");

        System.out.println("start:" + start);
        System.out.println("max:" + max);
        System.out.println("end:" + end);
        System.out.println("minStr :" + minStr);
        System.out.println("maxStr :" + maxStr);
        System.out.println("startTime :" + startTime);
        System.out.println("endTime :" + endTime);
        System.out.println("plus :" + plus);
        System.out.println("quarterMin :" + DateUtil
                .formatLocalDateTime(DateUtil.DATE_TIME_FORMAT, quarterMin));
        System.out.println("quarterMax :" + DateUtil
                .formatLocalDateTime(DateUtil.DATE_TIME_FORMAT, quarterMax));
        System.out.println(JsonUtil.toJSONString(getYearMonthListOfQuarter("2021q3")));
        System.out.println(getMonthOfQuarter());
        System.out.println(getQuarterOrMonthStr(202104L, 202106L));
    }
}
